﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace RulesCore
{

        public class FormulaEvaluator
        {
            private readonly Regex bracketsRegex = new Regex(@"([a-z]*)\(([^\(\)]+)\)(\^|!?)", RegexOptions.Compiled);
            private readonly Regex cosRegex = new Regex(@"cos(-?\d+.?\d*)", RegexOptions.Compiled);
            private readonly Regex sinRegex = new Regex(@"sin(-?\d+.?\d*)", RegexOptions.Compiled);
            private readonly Regex powerRegex = new Regex(@"(-?\d+\.?\d*)\^(-?\d+\.?\d*)", RegexOptions.Compiled);
            private readonly Regex multiplyRegex = new Regex(@"(-?\d+\.?\d*)\*(-?\d+\.?\d*)", RegexOptions.Compiled);
            private readonly Regex divideRegex = new Regex(@"(-?\d+\.?\d*)/(-?\d+\.?\d*)", RegexOptions.Compiled);
            private readonly Regex addRegex = new Regex(@"(-?\d+\.?\d*)\+(-?\d+\.?\d*)", RegexOptions.Compiled);
            private readonly Regex subtractRegex = new Regex(@"(-?\d+\.?\d*)-(-?\d+\.?\d*)", RegexOptions.Compiled);

            public double Evaluate(string expr)
            {
                expr = expr.Replace(" ", "").ToLower();

                Match m = bracketsRegex.Match(expr);
                while (m.Success)
                {
                    if (Solve(m.Groups[2].Value) == "Infinity")
                    {
                        return double.Parse("Infinity");
                    }
                    else if (Solve(m.Groups[2].Value) == "NaN")
                    {
                        return double.Parse("NaN");
                    }
                    else
                    {
                        expr = expr.Replace("(" + m.Groups[2].Value + ")", Solve(m.Groups[2].Value));
                        m = bracketsRegex.Match(expr);
                    }
                }

                return Convert.ToDouble(Solve(expr));
            }

            private string Solve(string expr)
            {
                if (expr.IndexOf("cos") != -1) expr = Do(cosRegex, expr, (x) =>
                    Math.Cos(Convert.ToDouble(x.Groups[1].Value)).ToString());

                if (expr.IndexOf("sin") != -1) expr = Do(sinRegex, expr, (x) =>
                    Math.Sin(Convert.ToDouble(x.Groups[1].Value)).ToString());

                if (expr.IndexOf("^") != -1) expr = Do(powerRegex, expr, (x) =>
                    Math.Pow(Convert.ToDouble(x.Groups[1].Value), Convert.ToDouble(x.Groups[2].Value)).ToString());

                if (expr.IndexOf("/") != -1) expr = Do(divideRegex, expr, (x) =>
                    (Convert.ToDouble(x.Groups[1].Value) / Convert.ToDouble(x.Groups[2].Value)).ToString());

                if (expr.IndexOf("*") != -1) expr = Do(multiplyRegex, expr, (x) =>
                    (Convert.ToDouble(x.Groups[1].Value) * Convert.ToDouble(x.Groups[2].Value)).ToString());

                if (expr.IndexOf("+") != -1) expr = Do(addRegex, expr, (x) =>
                    (Convert.ToDouble(x.Groups[1].Value) + Convert.ToDouble(x.Groups[2].Value)).ToString());

                if (expr.IndexOf("-") != -1) expr = Do(subtractRegex, expr, (x) =>
                    (Convert.ToDouble(x.Groups[1].Value) - Convert.ToDouble(x.Groups[2].Value)).ToString());

                return expr;
            }

            private static string Do(Regex regex, string formula, Func<Match, string> func)
            {
                MatchCollection collection = regex.Matches(formula);

                if (collection.Count == 0) return formula;

                for (int i = 0; i < collection.Count; i++)
                {

                        formula = formula.Replace(collection[i].Groups[0].Value, func(collection[i]));

                }
                formula = Do(regex, formula, func);

                return formula;
            }
        }
}
